package cn.xerllent.common.service.impl;

import java.util.Collections;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.template.Template;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.TemplateUtil;
import cn.xerllent.common.entity.VerifyCode;
import cn.xerllent.common.entity.vo.EmailVo;
import cn.xerllent.common.exception.BadRequestException;
import cn.xerllent.common.service.AliyunSmsService;
import cn.xerllent.common.service.VerifyCodeService;
import cn.xerllent.common.service.mapper.VerifyCodeMapper;

/**
 * 验证码数据库服务
 */
@Service
//@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public class VerifyCodeServiceImpl extends BaseServiceImpl<VerifyCodeMapper, VerifyCode> implements VerifyCodeService {
	
	@Autowired
	private AliyunSmsService aliyunSmsService;
	
	@Autowired
	@Qualifier("singleThreadExecutor")
	private ExecutorService singleThreadExecutor;

    @Value("${code.expiration}")
    private Integer expiration;
    
    private ScheduledExecutorService executorService;
    private final Object lock = new Object();
    
    public ScheduledExecutorService getScheduledExecutorService() {
    	synchronized (lock) {
    		if(executorService==null) {
    			executorService = new ScheduledThreadPoolExecutor(1,
    	                new BasicThreadFactory.Builder().namingPattern("verifyCode-schedule-pool-%d").daemon(true).build());
    		}
		}
    	return executorService;
    }
    
	@Override
	public void sendSms(VerifyCode code) {
		if(ObjectUtil.isEmpty(code.getCode())) {
			code.setCode(RandomUtil.randomNumbers(6));
		}
        this.save(code);
        timedDestruction(code);
        
		singleThreadExecutor.execute(new Runnable() { //改善性能，利用单线程任务执行
			
			@Override
			public void run() {
				try {
					aliyunSmsService.sendVerifySms(code.getValue(), code.getCode());
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

    @Override
//    @Transactional(rollbackFor = Exception.class)
    public EmailVo sendEmail(VerifyCode code) {
        EmailVo emailVo;
        String content;
        VerifyCode verificationCode = this.lambdaQuery()
        		.eq(VerifyCode::getScenes, code.getScenes())
        		.eq(VerifyCode::getType, code.getType())
        		.eq(VerifyCode::getValue, code.getValue())
        		.eq(VerifyCode::getStatus, true)
        		.orderByDesc(VerifyCode::getId).last("limit 1").one();

//        VerifyCode verificationCode = this.getOne(new LambdaQueryWrapper<VerifyCode>()
//                .eq(VerifyCode::getScenes, code.getScenes()).eq(VerifyCode::getType, code.getType()).eq(VerifyCode::getValue, code.getValue()));
        
        TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("templates", TemplateConfig.ResourceMode.CLASSPATH));
        Template template = engine.getTemplate("email/email.ftl");
        if (verificationCode == null) {
        	// 如果不存在有效的验证码，就创建一个新的
            code.setCode(RandomUtil.randomNumbers(6));
            content = template.render(Dict.create().set("code", code.getCode()));
            emailVo = new EmailVo(Collections.singletonList(code.getValue()), "验证码邮件", content);
            this.save(code);
            timedDestruction(code);
            // 存在就再次发送原来的验证码
        } else {
            content = template.render(Dict.create().set("code", verificationCode.getCode()));
            emailVo = new EmailVo(Collections.singletonList(verificationCode.getValue()), "验证码邮件", content);
        }
        return emailVo;
    }
    

    @Override
    public void validated(VerifyCode code) {
    	VerifyCode verificationCode = this.lambdaQuery()
    			.eq(VerifyCode::getScenes, code.getScenes())
    			.eq(VerifyCode::getType, code.getType())
    			.eq(VerifyCode::getValue, code.getValue())
    			.eq(VerifyCode::getCode, code.getCode())
    			.eq(VerifyCode::getStatus, true)
    			.orderByDesc(VerifyCode::getId).last("limit 1").one();
//        VerifyCode verificationCode = this.getOne(new LambdaQueryWrapper<VerifyCode>()
//                .eq(VerifyCode::getScenes, code.getScenes()).eq(VerifyCode::getType, code.getType()).eq(VerifyCode::getValue, code.getValue())
//                .eq(VerifyCode::getStatus, true));
        if (verificationCode == null) {
            throw new BadRequestException("无效验证码");
        } else {
            verificationCode.setStatus(false);
            this.saveOrUpdate(verificationCode);
        }
    }

    /**
     * 定时任务，指定分钟后改变验证码状态
     * @param verifyCode 验证码
     */
    private void timedDestruction(VerifyCode verifyCode) {
        //以下示例为程序调用结束继续运行
        try {
        	getScheduledExecutorService().schedule(() -> {
                verifyCode.setStatus(false);
                this.save(verifyCode);
            }, expiration * 60 * 1000L, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}
